﻿module Preprocessor.Prep

open Clexer
open PrintUtils
open Cabs

type HistoryType = 
    | Delete of string*cabsloc
    | Insert of string*cabsloc
    | Changed of string*string*cabsloc

type Context() = 
    let definesMap = new System.Collections.Generic.Dictionary<string,int*string list*token list>(6000)
    let searchDirs = new ResizeArray<string>()
    
    member this.DefinesMap  = definesMap
    member this.SearchDirs = searchDirs

type Prep (sourceFilename:string) =

    let (lexemeStack:token list ref) = ref (List.empty)
    let history = ref (List.empty :> List<HistoryType>) 
    let mutable sourceFile = System.IO.Path.GetFileName sourceFilename
    let mutable fullSourceFilename = sourceFilename

    member this.LexemeStack with get () = !lexemeStack and set v = lexemeStack := v 
    member this.History with get () = !history and set v = history := v
    member this.SourceFile with get () = sourceFile and set v = sourceFile <- v
    member this.FullSourceFilename with get () = fullSourceFilename and set v = fullSourceFilename <- v

    member this.GetNextLexeme lexemes =
        let next lexemes =
            match (this.LexemeStack) with
                | [] -> Seq.head lexemes                    
                | x::y -> this.LexemeStack <- y
                          x
        try           
            let nextLexeme = next lexemes
            let secondLexeme =  try Some(next lexemes) with e -> None
            match secondLexeme with 
                | Some(DIRECTIVE_CONCAT (_)) -> 
                   let concatenatedLexeme = next lexemes
                   let loc = GetLoc nextLexeme 
                   let lexemes = Lexer.runLexerFromString loc.filename loc.lineno ((TokenToString nextLexeme) + (TokenToString concatenatedLexeme)) 
                   this.LexemeStack <- List.append lexemes.Tail  (this.LexemeStack)
                   Some(lexemes.Head)
                | Some x -> this.LexemeStack <- x::(this.LexemeStack)
                            Some(nextLexeme)
                | None ->  Some(nextLexeme)
        with e ->
            None

    member this.GetNextLexemeWithoutConcatenate lexemes =
        try
           match (this.LexemeStack) with
                | [] -> Some(Seq.head lexemes)              
                | x::y -> this.LexemeStack <- y
                          Some x
        with e ->
            None

   
    member this.GetNextLexemeFromSeq lexemes = Seq.head lexemes

    member this.takeWhile predicate lexemes = 
            seq {
                    let value = this.GetNextLexemeWithoutConcatenate lexemes
                    if value.IsSome then 
                        let lexeme = ref (value.Value)  
                        while ((!lexeme <> EOF(Cabshelper.cabslu)) && (predicate !lexeme)) do
                            yield !lexeme
                            let value = this.GetNextLexemeWithoutConcatenate lexemes
                            if value.IsSome then
                                lexeme := value.Value
                            else 
                                lexeme := EOF(Cabshelper.cabslu)
                        this.LexemeStack <- !lexeme::(this.LexemeStack);
                }